/***************************************************************************
 *	@file 		fetion_login.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of login operation, contain login thread function
 *				object, SSL client and hash password generator
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_login.h"
#include "fetion_contact.h"
#include "fetion_user.h"
#include "fetion_exception.h"
#include "fetion_verify_code.h"
#include "fetion_config.h"
#include "fetion_sip.h"

boost::asio::io_service io_service;
extern boost::mutex user_mutex;

int loginThreadCallable::login_action()
{
	currentUser *user;
	Config *config = Config::get_instance();
	Glib::ustring ssi_res;
	Glib::ustring config_res;
	std::string sip_res, response;
	std::string nonce, key;
	unsigned int try_times = 1;
	char *aeskey = NULL;
	int ret = FUNC_SUCC;
	
	try {
		WRITE_LOG(LOG_TRACE, "create login thread.");
		user = currentUser::get_instance(login_no, login_passwd);
		sysinfoString sstr("-- SSI login... (1/5)\n");
		stdout<<sstr;

		do {
			user->state = login_state;
//			std::cout<<"user->state = "<<user->state<<std::endl;
			if(FUNC_SUCC != SSI_auth_action(ssi_res)) {
				throw fetionException("ssi authentication action failed.");
			}
			WRITE_LOG(LOG_TRACE, "SSI authentication action succeed.");
//			std::cout<<"ssi_res = "<<std::endl<<ssi_res<<std::endl;
			if(FUNC_SUCC != parse_ssi_response(ssi_res)) {
				throw fetionException("parse ssi response failed.");
			}
			WRITE_LOG(LOG_TRACE, "parse ssi reponse succeed.");

			if(421 == user->loginStatus || 420 ==  user->loginStatus) {
				if(try_times > 1) {
					errorString estr("input verification code is wrong.\n");
					stdout<<estr;
				}
				if(FUNC_SUCC != input_verify_code()) {
					throw fetionException("input verification code failed.");
				}
				try_times++;
			}
			else if(401 == user->loginStatus || 400 ==  user->loginStatus || 404 ==  user->loginStatus) {
				errorString estr("password ERROR!\n");
				stdout<<estr;
				throw fetionException("input password error.");
			}
			else if(200 == user->loginStatus) {
				break;
			}
			else {
				throw fetionException("unknow login status code.");
			}
		}while(true);

		userlist *ul = config->user_list;
		if(NULL == ul) {
			throw fetionException("get userlist pointer failed.");
		}
		userlist::userinfo ui;
		user->get_userinfo(ui);
		ul->update(ui);
		WRITE_LOG(LOG_TRACE, "update userlist in local database.");

		sstr("-- download configuration... (2/5)\n");
		stdout<<sstr;
		if(FUNC_SUCC != config->init(user->userId)) {
			throw fetionException("configuration initial failed.");
		}
		user->config = config;

		if(FUNC_SUCC != config->load_from_database()) {
			throw fetionException("load config from database failed.");
		}

		if(FUNC_SUCC != download_configuration(config_res)) {
			throw fetionException("download configuration failed.");
		}
		WRITE_LOG(LOG_TRACE, "download configuration from server.");

		if(FUNC_SUCC != config->parse_config_xml(config_res)) {
			throw fetionException("parse configuration response xml failed.");
		}
		WRITE_LOG(LOG_TRACE, "parse configuration response xml.");
		
		if(FUNC_SUCC != config->save_to_database()) {
			throw fetionException("save configuration to database failed.");
		}

		sstr("-- load user information from database... (3/5)\n");
		stdout<<sstr;
		if(FUNC_SUCC != user->load_from_database()) {
			throw fetionException("load user information from database failed.");
		}
		if(FUNC_SUCC != user->load_contacts_from_db()) {
			throw fetionException("load contacts information from database failed.");
		}
		sstr("-- register to sip server... (4/5)\n");
		stdout<<sstr;
		FetionSip *sip = new FetionSip(user->sId);
		user->sip = sip;
		WRITE_LOG(LOG_TRACE, "connect to sip server : %s:%d.", 
			config->sipcProxyIP.c_str(), config->sipcProxyPort);
		if(FUNC_SUCC != sip->connect(config->sipcProxyIP, config->sipcProxyPort)) {
			throw fetionException("connect to sip server failed.");
		}
		if(FUNC_SUCC != sipc_reg_action(sip_res)) {
			throw fetionException("sip register action failed.");
		}
//		std::cout<<sip_res<<std::endl;
		if(FUNC_SUCC != parse_sipc_reg_response(sip_res, nonce, key)) {
			throw fetionException("parse sip register response failed.");
		}
		WRITE_LOG(LOG_TRACE, "register to sip server succeed.");
//		std::cout<<nonce<<"|||"<<key<<std::endl;
		sstr("-- sipc authentication... (5/5)\n");
		stdout<<sstr;
		aeskey = generate_aes_key();
		if(FUNC_SUCC != generate_sip_response(nonce, key, aeskey, response)) {
			throw fetionException("generate sip response failed.");
		}
		WRITE_LOG(LOG_TRACE, "generate sip response succeed.");
		if(NULL != aeskey) {
			delete []aeskey; aeskey = NULL;
		}

		try_times = 0;
		do {
			sip_res.clear();
			if(FUNC_SUCC != sipc_auth_action(sip_res, response)) {
				throw fetionException("sip authentication failed.");
			}
			WRITE_LOG(LOG_TRACE, "get response of sip authentication.");
			if(FUNC_SUCC != parse_sipc_auth_response(sip_res)) {
				throw fetionException("parse sip authentication response failed.");
			}
			WRITE_LOG(LOG_TRACE, "parse sip authentication response succeed.");

			if(421 == user->loginStatus || 420 ==  user->loginStatus) {
				if(try_times > 1) {
					errorString estr("input verification code is wrong.\n");
					stdout<<estr;
				}
				if(FUNC_SUCC != input_verify_code()) {
					throw fetionException("input verification code failed.");
				}
				try_times++;
			}
			else if(401 == user->loginStatus || 400 ==  user->loginStatus || 404 ==  user->loginStatus) {
				errorString estr("password ERROR!\n");
				stdout<<estr;
				throw fetionException("input password error.");
			}
			else if(200 == user->loginStatus) {
				break;
			}
			else {
				throw fetionException("unknow login status code.");
			}
		}while(true);
		WRITE_LOG(LOG_TRACE, "sip login succeed.");
		if(NULL == user->find_group_by_id(BUDDY_LIST_NOT_GROUPED) && 
				NULL != user->find_contact_by_groupid(BUDDY_LIST_NOT_GROUPED)) {
			Group group(BUDDY_LIST_NOT_GROUPED, "Ungrouped");
			user->groupList.push_back(group);
		}
		if(NULL == user->find_group_by_id(BUDDY_LIST_STRANGER) && 
				NULL != user->find_contact_by_groupid(BUDDY_LIST_STRANGER)) {
			Group group(BUDDY_LIST_STRANGER, "Strangers");
			user->groupList.push_back(group);
		}
		if(FUNC_SUCC != user->save_to_database()) {
			throw fetionException("save user information to database failed.");
		}
		if(FUNC_SUCC != user->save_contacts_to_db()) {
			throw fetionException("save contact list to database failed.");
		}
		if(FUNC_SUCC != contact_subscribe_only()) {
			throw fetionException("contact subscribe failed.");
		}
		WRITE_LOG(LOG_TRACE, "send contacts subscribe request.");
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "login action failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int loginThreadCallable::SSI_auth_action(Glib::ustring &ssi_res) const
{
	int passwd_type;
	WRITE_LOG(LOG_TRACE, "start SSI authentication action.");
	std::stringstream sslbuf, verifyUri;
	boost::system::error_code error;
	currentUser *user = currentUser::get_instance();

	try {
		HashPassword hash_passwd(login_no, login_passwd);
		std::string passwd = hash_passwd.get_password();
		if(passwd.empty()) {
			throw fetionException("get hash password failed.");
		}
		else {
			WRITE_LOG(LOG_TRACE, "get hash password : %s.", passwd.c_str());
		}

		if(user->Verified()) {	// need this while neet verification
			verifyUri<<"&pid="<<user->ver->guid<< 			// get from verification response
					   "&pic="<<user->ver->code<< 			// user input words in picture get from verification response
					   "&algorithm="<<user->ver->algorithm;	// get from ssi authentication response
		}
		passwd_type = (user->userId.empty() ? 1 : 2);
		sslbuf.str("");
		sslbuf<<"GET /ssiportal/SSIAppSignInV4.aspx?mobileno="<<login_no<<
			    "&domains=fetion.com.cn"<<verifyUri.str()<<
			    "&v4digest-type="<<passwd_type<<
			    "&v4digest="<<passwd<<"\r\n"<<
			    "User-Agent: IIC2.0/pc 4.0.2510\r\n"<<
				"Host: uid.fetion.com.cn\r\n"<<
			    "Cache-Control: private\r\n"<<
			    "Connection: Keep-Alive\r\n\r\n";
		
		boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
		tcp::resolver resolver(io_service);
		tcp::resolver::query query("uid.fetion.com.cn", "443");
		tcp::resolver::iterator iterator = resolver.resolve(query, error);
		if(error) {
			throw boost::system::system_error(error);
		}
		WRITE_LOG(LOG_TRACE, "connect to ssi server : uid.fetion.com.cn:443");
		SSLclient ssl(io_service, ctx, iterator);

		if(FUNC_SUCC != ssl.get_ssi_response(sslbuf.str(), ssi_res)) {
			throw fetionException("get ssi response failed.");
		}
		
		return FUNC_SUCC;
	}
	catch (boost::system::system_error &se) {
		WRITE_LOG(LOG_FATAL, "create SSI connection failed : %s", se.what());
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "SSI authentication action failed : %s", ex.what());
	}
	
	return FUNC_FAIL;
}

int loginThreadCallable::parse_ssi_response(const std::string &ssi_res) const
{
	xmlpp::DomParser xml_parser;
	xmlpp::Node *pNode;
	currentUser *user = currentUser::get_instance();
	Glib::ustring::size_type pos;
	Glib::ustring xml;
	Glib::ustring tmp;
	
	try {
		if((pos = ssi_res.find("\r\n\r\n", 0)) == std::string::npos) {
			throw fetionException("ssi response has no string \"\r\n\r\n\".");
		}
		xml = ssi_res.substr(pos+4, ssi_res.length()-pos-4);
		if((pos = ssi_res.find("ssic=", 0)) != std::string::npos) {
			user->ssic = ssi_res.substr(pos+5, ssi_res.find(";", pos+5)-pos-5);
		}

		xml_parser.parse_memory(xml);
		pNode = xml_parser.get_document()->get_root_node();
		const xmlpp::Element *elem = dynamic_cast<const xmlpp::Element*>(pNode);
		tmp = elem->get_attribute_value("status-code");
		if(tmp.empty()) {
			throw fetionException("get attribute value failed.");
		}
		user->loginStatus = atoi(tmp.c_str());

		if(200 ==  user->loginStatus || 420 == user->loginStatus || 421 ==  user->loginStatus) {
			pNode = *(pNode->get_children().begin());
			if(NULL == pNode) {
				throw fetionException("get children node failed.");
			}

			if(200 == user->loginStatus) {
				WRITE_LOG(LOG_TRACE, "SSI login succeed.");
				user->parse_ssi_succ(pNode);
			}
			else {
				WRITE_LOG(LOG_TRACE, "SSI login need verification.");
				user->parse_ssi_fail(pNode);
			}
		}
		
		return FUNC_SUCC;
	}
	catch (xmlpp::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse xml of ssi response failed : %s.", ex.what());
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse ssi response failed : %s.", ex.what());
	}
	
	return FUNC_FAIL;
}

int loginThreadCallable::input_verify_code() const
{
	int ret = FUNC_SUCC;
	std::string input;
	currentUser *user = currentUser::get_instance();
	Config *config = Config::get_instance();
	std::string pic_path(config->global_path + "/code.gif");
	VerifyCode verify_code(pic_path, user->ver->text, user->ver->tips);

	try {
		if(FUNC_SUCC != verify_code.generate_pic_code()) {
			throw fetionException("generate verification code picture failed.");
		}

		sysinfoString sstr("%s\n%s\n", verify_code.reason.c_str(), verify_code.tip.c_str());
		stdout<<sstr;

		do {
			input.clear();
			sstr("verification code picture path: %s, input what you see:", verify_code.pic_path.c_str());
			stdout<<sstr;
			std::cin>>input;
			if(input.length() >= 3 && input.length() <= 8) {
				break;
			}
			errorString estr("invalid input, please input again.\n");
			stdout<<estr;
		} while(true);

		user->ver->code = input;
		WRITE_LOG(LOG_TRACE, "user input verification code: %s.", input.c_str());
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "input verification code failed : %s", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

int loginThreadCallable::download_configuration(Glib::ustring &config_res) const
{
	int ret = FUNC_SUCC;
	currentUser *user = currentUser::get_instance();
	Config *config = user->config;
	std::string uri = "nav.fetion.com.cn";
	std::stringstream request;
	std::string body;
	unsigned int len = 0;
	char header[1024] = {0};
	int bodylen;
	Glib::ustring tmp;
	std::string::size_type pos;
	char *body_;
	
	try {
		if(FUNC_SUCC != config->generate_config_body(body)) {
			throw fetionException("generate config body failed.");
		}
		request<<"POST /nav/getsystemconfig.aspx HTTP/1.1\r\n"<<
				 "User-Agent: IIC2.0/PC 4.0.2510\r\n"<<
				 "Host: "<<uri<<"\r\n"<<
				 "Connection: Close\r\n"<<
				 "Content-Length: "<<body.length()<<"\r\n\r\n"<<body;

		boost::system::error_code error;
		tcp::resolver resolver(io_service);
		tcp::resolver::query query(uri, "80");
		tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, error);
		if(error) {
			throw fetionException("resolve endpoint of %s failed.", uri.c_str());
		}
		tcp::resolver::iterator end;
		tcp::socket socket(io_service);
		do {
			socket.close();
			socket.connect(*endpoint_iterator, error);
#ifdef FETION_LOG
			if(!error) {
				tcp::endpoint endpoint = *endpoint_iterator;
				std::stringstream ip_address;
				ip_address<<endpoint;
				WRITE_LOG(LOG_TRACE, "get ip address of configuration server : %s.", ip_address.str().c_str());
			}
#endif
			endpoint_iterator++;
		} while (error && endpoint_iterator != end);
		if (error) {
			throw boost::system::system_error(error);
		}
		
		len = boost::asio::write(socket, boost::asio::buffer(request.str()));
		if(len <= 0) {
			throw fetionException("length of write buffer error.");
		}

		do {
			memset(header, 0, sizeof(header));
			boost::asio::read(socket, boost::asio::buffer(header, 1023), 
				boost::asio::transfer_at_least(20), error);
			if(error) {
				throw boost::system::system_error(error);
			}
		} while(!strstr(header, "\r\n\r\n"));
//		std::cout<<header<<std::endl;
		std::string header_str = std::string(header);

		if((pos = header_str.find("Content-Length: ", 0)) == std::string::npos) {
			throw fetionException("can't get body length from response header.");
		}
		tmp = header_str.substr(pos+16, header_str.find("\r\n", pos+16)-pos-16);
		bodylen = atoi(tmp.c_str());
		tmp.clear();

		if((pos = header_str.find("\r\n\r\n", 0)) != std::string::npos) {
			tmp = header_str.substr(pos+4, header_str.length()-pos-4);
		}

		body_ = new char[bodylen+1];
		if(!body_) {
			throw fetionException("memory allocation of body response failed.");
		}
		memset(body_, 0, bodylen+1);
		len = boost::asio::read(socket, boost::asio::buffer(body_, bodylen), 
			boost::asio::transfer_at_least(bodylen-tmp.bytes()), error);
		if(error) {
			throw boost::system::system_error(error);
		}
		if(len != bodylen-tmp.bytes()) {
			throw fetionException("response body length not equal to length in header.");
		}
		config_res = tmp + body_;
		socket.close();
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "download configuration failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	
	if(NULL != body_) {
		delete []body_;
		body_ = NULL;
	}
	return ret;
}

int loginThreadCallable::sipc_reg_action(std::string &sipc_res) const
{
	int ret = FUNC_SUCC;
	std::string cnonce;
	currentUser *user = currentUser::get_instance();
	FetionSip *sip = user->sip;
	std::string sipmsg;

	try {
		cnonce = generate_cnonce();
		sip->setType(FetionSip::SIP_REGISTER);
		sip->addHeader("CN", cnonce);
		sip->addHeader("CL", "type=\"pc\" ,version=\"4.0.2510\"");
		if(FUNC_SUCC != sip->get_send_string(sipmsg)) {
			throw fetionException("get sip sending string failed.");
		}
		WRITE_LOG(LOG_TRACE, "start registering to sip server.");
		sip->send_sip_message(sipmsg);
		if(FUNC_SUCC != sip->receive_sip_response(sipc_res)) {
			throw fetionException("get sip response failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "sip register failed : %s.", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

int loginThreadCallable::parse_sipc_reg_response(const std::string sipres, 
	std::string &nonce, std::string &key) const
{
	int ret = FUNC_SUCC;
	std::string digest;
	currentUser *user = currentUser::get_instance();
	FetionSip *sip = user->sip;
	std::string::size_type pos;
	std::string tmp;
	
	try {
		if(FUNC_SUCC != sip->get_attribute_value(sipres, "W", digest)) {
			throw fetionException("get attribute 'digest' failed.");
		}
		if((pos = digest.find("nonce",0)) == std::string::npos) {
			throw fetionException("get nonce failed.");
		}
		tmp = digest.substr(pos+7, digest.length()-pos-7);
		nonce = tmp.substr(0, tmp.find("\",", 0));

		if((pos = digest.find("key", 0)) == std::string::npos) {
			throw fetionException("get key failed.");
		}
		tmp = digest.substr(pos+5, digest.length()-pos-5);
		key = tmp.substr(0, tmp.find("\",", 0));
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse sipc register response failed : %s.", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

std::string loginThreadCallable::generate_cnonce() const
{
	char* cnouce = new char[33];//malloc(33);
	memset( cnouce , 0 , 33 );
	sprintf( cnouce , "%04X%04X%04X%04X%04X%04X%04X%04X" , 
			rand() & 0xFFFF , rand() & 0xFFFF , 
			rand() & 0xFFFF , rand() & 0xFFFF ,
			rand() & 0xFFFF , rand() & 0xFFFF,
			rand() & 0xFFFF , rand() & 0xFFFF );
	std::string cnouce_str = std::string(cnouce);
	delete []cnouce;
	return cnouce_str;
}

char* loginThreadCallable::generate_aes_key() const
{
	char* key = new char[65];
	if(key == NULL){
		return NULL;
	}
	memset( key , 0 , 65 );
	FILE* rand_fd = fopen("/dev/urandom", "r");
	if(rand_fd == NULL){
		free(key);
		return NULL;
	}
	int ret = fread(key, 64, 1, rand_fd);
	if(ret != 1){
		free(key);
		fclose(rand_fd);
		return NULL;
	}
	fclose(rand_fd);
	return key;
}

int loginThreadCallable::generate_sip_response(const std::string &nonce,
	const std::string publickey, const char* key, std::string &response) const
{
	int ret = FUNC_SUCC;
	currentUser *user = currentUser::get_instance();
	int psd_len, aeskey_len, nonce_len;
	std::string psdhex, modulus, exponent;
	unsigned char *psd = NULL, *aeskey = NULL, *res = NULL, *out = NULL;
	
	try {
		HashPassword passwd(user->userId, user->password);
		psdhex = passwd.get_password();
		modulus = publickey.substr(0, 256);
		exponent = publickey.substr(256, 6);
		nonce_len = nonce.length();
		psd = strtohex(psdhex.c_str(), &psd_len);
		aeskey = strtohex(key, &aeskey_len);
		if(NULL == psd || NULL == aeskey) {
			throw fetionException("generate hex code of psd and aeskey failed.");
		}
		res = new unsigned char[nonce_len + aeskey_len + psd_len + 1];
		if(NULL == res) {
			throw fetionException("memory allocation of res failed.");
		}
		memset(res , 0 , nonce_len + aeskey_len + psd_len + 1);
		memcpy(res , nonce.c_str() , nonce_len);
		memcpy(res + nonce_len , psd , psd_len );
		memcpy(res + nonce_len + psd_len , aeskey , aeskey_len);	
		
		RSA *r = RSA_new();
		BIGNUM *bnn, *bne;
		int ret, flen;
		bnn = BN_new();
		bne = BN_new();
		BN_hex2bn(&bnn, modulus.c_str());
		BN_hex2bn(&bne, exponent.c_str());
		r->n = bnn;	r->e = bne;	r->d = NULL;
		flen = RSA_size(r);
		out =  new unsigned char[flen];
		if(NULL == out) {
			throw fetionException("memory allocation of out failed.");
		}
		memset(out , 0 , flen);
		
		ret = RSA_public_encrypt(nonce_len + aeskey_len + psd_len,
				res, out, r, RSA_PKCS1_PADDING);
		if(ret < 0) {
			throw fetionException("Encrypt response failed!");
		}
		RSA_free(r);
		response = hextostr(out, ret);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "generate sip response : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	if(NULL != psd) {
		delete []psd; psd = NULL;
	}
	if(NULL != aeskey) {
		delete []aeskey; aeskey = NULL;
	}
	if(NULL != res) {
		delete []res; res = NULL;
	}
	if(NULL != out) {
		delete []out; out = NULL;
	}
	return ret;
}

int loginThreadCallable::sipc_auth_action(std::string &sip_res, const std::string &response) const
{
	int ret = FUNC_SUCC;
	std::string xml;
	currentUser *user = currentUser::get_instance();
	FetionSip *sip = user->sip;
	std::stringstream aheader, ack;
	Verification *ver = user->ver;
	std::string sipmsg;
	
	try {
		if(FUNC_SUCC != user->get_sip_auth_body(xml)) {
			throw fetionException("get sip authentication body failed.");
		}
		sip->setType(FetionSip::SIP_REGISTER);
		aheader<<"Digest response=\""<<response<<"\",algorithm=\"SHA1-sess-v4\"";
		sip->addHeader("A", aheader.str());
		sip->addHeader("AK", "ak-value");
		if(NULL != ver && !ver->algorithm.empty()) {
			ack<<"Verify response=\""<<ver->code<<
				 "\",algorithm=\""<<ver->algorithm<< 
				 "\",type=\""<<ver->type<<
				 "\",chid=\""<<ver->guid<<"\"";
			sip->addHeader("A", ack.str());
		}
		if(FUNC_SUCC != sip->get_send_string(sipmsg, xml)) {
			throw fetionException("get sip string failed.");
		}
		sip->send_sip_message(sipmsg);
		if(FUNC_SUCC != sip->receive_sip_response(sip_res)) {
			throw fetionException("get sip response failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "sip authentication failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int loginThreadCallable::parse_sipc_auth_response(const std::string &sip_res) const
{
	int ret = FUNC_SUCC;
	int code;
	std::string::size_type pos;
	std::string tmp1, tmp2;
	currentUser *user = currentUser::get_instance();
	FetionSip *sip = user->sip;
	xmlParser parser;
	xmlpp::Document *doc = NULL;
	xmlpp::Node *rootnode = NULL, *node = NULL, *node1 = NULL;
	xmlpp::Element *elem;
	unsigned int local_group_count, local_contact_count;

	try {
		code = sip->get_status_code(sip_res);
		user->loginStatus = code;

		if(200 == code) {
			if(NULL != user->ver) {
				delete user->ver;
				user->ver = NULL;
			}
			WRITE_LOG(LOG_TRACE, "sip authentication succeed.");
		}
		else if(421 == code || 420 == code) {
			if(FUNC_SUCC != user->parse_add_buddy_verification(sip_res)) {
				throw fetionException("parse add buddy verfication failed.");
			}
			return ret;
		}
		else {
			throw fetionException("sip authentication failed.");
		}
		if((pos = sip_res.find("\r\n\r\n", 0)) == std::string::npos) {
			throw fetionException("can't get xml body.");
		}
		tmp1 = sip_res.substr(pos+4, sip_res.length()-pos-4);
//		std::cout<<tmp1<<std::endl;
		parser.parse_memory(tmp1);
		doc = parser.get_document();
		if(NULL == doc) {
			throw fetionException("can't parse xml from memory.");
		}
		rootnode = doc->get_root_node();
		xmlpp::Node::NodeList children_list = rootnode->get_children();
		if(0 == children_list.size()) {
			throw fetionException("get children node list failed.");
		}
		xmlpp::Node::NodeList::const_iterator iter = children_list.begin();
		elem = dynamic_cast<xmlpp::Element *>(*iter);
		tmp2 = elem->get_attribute_value("public-ip");
		if(!tmp2.empty()) {
			user->publicIp = tmp2;			
		}
		tmp2.clear();
		tmp2 = elem->get_attribute_value("last-login-ip");
		if(!tmp2.empty()) {
			user->lastLoginIp = tmp2;			
		}
		tmp2.clear();
		tmp2 = elem->get_attribute_value("last-login-time");
		if(!tmp2.empty()) {
			user->lastLoginTime = tmp2;			
		}

		if(++iter == children_list.end()) {
			throw fetionException("get children node list failed.");
		}
		node = *iter;
		children_list = node->get_children();
		iter = children_list.begin();
		user->parse_personal_info(*iter);
		
		node1 = parser.get_specify_node(node, "custom-config");
		elem = dynamic_cast<xmlpp::Element *>(node1);
		tmp2.clear();
		tmp2 = elem->get_attribute_value("version");
		if(!tmp2.empty()) {
			user->customConfigVersion = tmp2;
		}
		tmp2.clear();
		tmp2 = parser.xml_get_text_content(node1);
		if(!tmp2.empty()) {
			user->customConfig = tmp2;
		}

		local_group_count = user->groupList.size();
		local_contact_count = user->contactList.size();
		node1 = parser.get_specify_node(node, "contact-list");
		if(NULL != node1) {
			if(FUNC_SUCC != user->parse_contact_list(node1)) {
				throw fetionException("parse contact list failed.");
			}
		}

		node1 = parser.get_specify_node(node, "chat-friends");
		if(NULL != node1) {
			user->parse_stranger_list(node1);
		}

		if(0 == user->groupCount) {
			user->groupCount = local_group_count;
		}
		else if(user->groupCount != local_contact_count) {
			user->cleanup_group_list();
		}

		if(0 == user->contactCount) {
			user->contactCount = local_contact_count;
		}
		else if(user->contactCount != local_contact_count) {
			user->cleanup_contact_list();
		}
		
		node1 = parser.get_specify_node(node, "quota-frequency");
		if(node1) {
			user->parse_sms_frequency(node1);
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse sipc authentication response failed : %s.", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

int loginThreadCallable::contact_subscribe_only() const
{
	int ret = FUNC_SUCC;
	FetionSip *sip = currentUser::get_instance()->sip;
	std::string body, msg;
	
	try {
		sip->setType(FetionSip::SIP_SUBSCRIPTION);
		sip->addEventHeader(SIP_EVENT_PRESENCE);
		body = generate_subscribe_body("0");
		if(body.empty()) {
			throw fetionException("generate subscribe body failed.");
		}
		if(FUNC_SUCC != sip->get_send_string(msg, body)) {
			throw fetionException("get sending string failed.");
		}
		sip->send_sip_message(msg);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "send contact subscribe request failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;

}

std::string loginThreadCallable::generate_subscribe_body(const std::string version) const
{
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *node;
	xmlpp::Element *elem;
	std::string body = "<args></args>";
	std::string res;

	parser.parse_memory(body);
	doc = parser.get_document();
	node = doc->get_root_node();
	elem = node->add_child("subscription");
	elem->set_attribute("self", "v4default;mail-count");
	elem->set_attribute("buddy", "v4default");
	elem->set_attribute("version", version);
	res = doc->write_to_string();

	xml_convert(res);
	return res;
}

SSLclient::SSLclient(boost::asio::io_service &io_service_ssl, 
	boost::asio::ssl::context &ctx, tcp::resolver::iterator endpoint_iterator)
	: ssl_sock(io_service_ssl, ctx) 
{
	try {
		tcp::resolver::iterator end;
		boost::system::error_code error = boost::asio::error::host_not_found;

	 	while(error && endpoint_iterator != end) {
			ssl_sock.lowest_layer().close();
			tcp::endpoint endpoint = *endpoint_iterator;
			std::stringstream ip_address;
			ip_address<<endpoint;
			WRITE_LOG(LOG_TRACE, "get ip address of ssi server : %s.", ip_address.str().c_str());
			ssl_sock.lowest_layer().connect(*endpoint_iterator++, error);
		}
		if (error) {
			throw boost::system::system_error(error);
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "create SSL connection for ssi failed : %s", ex.what());
	}
}

int SSLclient::get_ssi_response(const std::string request, Glib::ustring &response)
{
	char response_buf[1024] = {0};
	int ret;
	boost::system::error_code error;

	try {
		ssl_sock.handshake(boost::asio::ssl::stream_base::client, error);
		if(error) {
			throw boost::system::system_error(error);
		}
		ret = boost::asio::write(ssl_sock, boost::asio::buffer(request));
		if(ret <= 0) {
			throw fetionException("length of write buffer error.");
		}
		ret = ssl_sock.read_some(boost::asio::buffer(response_buf, 1023), error);
		if(error) {
			throw boost::system::system_error(error);
		}
		if(ret <= 0) {
			throw fetionException("length of read buffer error.");
		}
		response = std::string(response_buf);
		return FUNC_SUCC;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "get ssi response failed : %s", ex.what());
		return FUNC_FAIL;
	}
}

std::string HashPassword::get_password()
{
	const char* domain = "fetion.com.cn:";
	std::string res, dst;
	unsigned char* udomain, *upassword;
	currentUser *user = currentUser::get_instance();
	
	try {
		udomain = new unsigned char[strlen(domain)];
		upassword = new unsigned char[login_passwd.length()];
		if(NULL == udomain || NULL == upassword) {
			throw fetionException("memory allocation failed.");
		}
		memset(udomain , 0 , strlen(domain));
		memcpy(udomain , (unsigned char*)domain , strlen(domain));
		memset(upassword , 0 , login_passwd.length());
		memcpy(upassword , (unsigned char*)login_passwd.c_str() , login_passwd.length());

		res = hash_password_v1(udomain, strlen(domain), upassword, login_passwd.length());

		if(res.empty()) {
			throw fetionException("get hash password v1 failed.");
		}
		delete []udomain;
		delete []upassword;

		if(user->userId.empty()) {
			return res;
		}
		dst = hash_password_v2(user->userId, res);
		if(dst.empty()) {
			throw fetionException("get hash password v2 failed.");
		}
		return dst;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "get hash password failed : %s", ex.what());
		if(NULL != udomain) delete []udomain;
		if(NULL != upassword) delete []upassword;
		return std::string("");
	}
}

std::string HashPassword::hash_password_v1(const unsigned char* b0 , 
	int b0len , const unsigned char* password , int psdlen)
{
	unsigned char* dst;	
	unsigned char tmp[20];
	std::string res;

	try {
		dst = new unsigned char[b0len + psdlen + 1];
		if(NULL == dst) {
			throw fetionException("memory allocation failed.");
		}
		memset(tmp , 0 , sizeof(tmp));
		memset(dst , 0 , b0len + psdlen + 1);
		memcpy(dst , b0 , b0len);
		memcpy(dst + b0len , password , psdlen);
		/* dst = "fetion.com.cn:********" ('*' is password) */
		SHA_CTX ctx;
		SHA1_Init(&ctx);
		/* get sha1 password and put it to 'tmp' */
		SHA1_Update(&ctx , dst , b0len + psdlen );
		SHA1_Final(tmp , &ctx);
		delete []dst;
		res = hextostr(tmp , 20);
		return res;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "get hash password v1 failed : %s", ex.what());
		return std::string("");
	}
}

std::string HashPassword::hash_password_v2(const std::string userid, const std::string passwordhex)
{
	unsigned char* bpsd = NULL;
	unsigned char* bid;
	std::string res;
	int bpsd_len, id;
	unsigned char ubid[4];
	
	try {
		id = atoi(userid.c_str());
		bid = (unsigned char*)(&id);
		/* transfer password got from hash_password_v1() back to hex value */
		bpsd = strtohex(passwordhex.c_str() , &bpsd_len);
		memcpy(ubid , bid , 4);
		res = hash_password_v1(ubid, sizeof(id), bpsd, bpsd_len);
		if(res.empty()) {
			throw fetionException("get hash password v1 failed.");
		}
		delete []bpsd;
		return res;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "get hash password v2 failed : %s", ex.what());
		if(NULL != bpsd) delete []bpsd;
		return std::string("");
	}
}

void Register::keep_alive(const boost::system::error_code& error)
{
	FetionSip *sip = currentUser::get_instance()->sip;
	std::string body, res;

	try {
#ifndef DONT_LOGIN
		boost::mutex::scoped_lock lock(user_mutex);
		sip->setType(FetionSip::SIP_REGISTER);
		WRITE_LOG(LOG_TRACE, "send a keep alive request.");
		sip->addEventHeader(SIP_EVENT_KEEPALIVE);
		body = generate_keep_alive_body();
		if(body.empty()) {
			throw fetionException("generate keep alive request body failed.");
		}
		if(FUNC_SUCC != sip->get_send_string(res, body)) {
			throw fetionException("get sending string failed.");
		}
		sip->send_sip_message(res);
#else
		for(int i = 0; i < 100; i++) {
			std::cout<<i;
		}
		std::cout<<std::endl;
#endif
		timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(secs));
		timer_.async_wait(boost::bind(&Register::keep_alive, 
				this, boost::asio::placeholders::error));
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "keep alive request failed : %s.", ex.what());	
	}
}

std::string Register::generate_keep_alive_body()
{
	std::string args = "<args></args>";
	std::string res;
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *node;
	xmlpp::Element *elem;

	parser.parse_memory(args);
	doc = parser.get_document();
	node = doc->get_root_node();
	elem = node->add_child("credentials");
	if(!elem) {
		return std::string();
	}
	elem->set_attribute("domains", "fetion.com.cn");
	res = doc->write_to_string();
	return res;
}
